﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;
using M3G;

/**
 * このテストはサンプリングに特殊な条件を付加したときのテスト
 * 補間値そのもののテストは別ファイル。
 * */

namespace MyTestProject {
    public class TestKeyframeSample {

        /**
         * ValidRangeで有効キーフレームを制限したときのテスト
         * */
        [Fact]
        public void TestValidRange () {
            var kseq = new KeyframeSequence (4, 1, KeyframeSequence.Step);
            kseq.RepeatMode = KeyframeSequence.Constant;
            kseq.Duration = 500;
            kseq.SetValidRange (1, 2);

            //                   time             value
            kseq.SetKeyframe (0, 100, new float[] { 1.0f });  // 無効
            kseq.SetKeyframe (1, 200, new float[] { 2.0f });
            kseq.SetKeyframe (2, 300, new float[] { 3.0f });
            kseq.SetKeyframe (3, 400, new float[] { 4.0f });  // 無効

            Assert.Equal (2.0f, kseq.Sample (100, 0));
            Assert.Equal (2.0f, kseq.Sample (200, 0));
            Assert.Equal (2.0f, kseq.Sample (250, 0));
            Assert.Equal (3.0f, kseq.Sample (300, 0));
            Assert.Equal (3.0f, kseq.Sample (500, 0));
            Assert.Equal (3.0f, kseq.Sample (700, 0));
        }



        /**
         * ValidRangeを(First > Last)に設定した時のテスト
         * さらにConstantモードに設定
         */
        [Fact]
        public void TestValidRangeConstant () {
            var kseq = new KeyframeSequence (4, 1, KeyframeSequence.Step);
            kseq.RepeatMode = KeyframeSequence.Constant;
            kseq.Duration = 500;
            kseq.SetValidRange (3, 0);

            //                   time             value
            kseq.SetKeyframe (0, 200, new float[] { 1.0f });
            kseq.SetKeyframe (1, 300, new float[] { 2.0f });  // 無効
            kseq.SetKeyframe (2, 400, new float[] { 3.0f });  // 無効
            kseq.SetKeyframe (3, 100, new float[] { 4.0f });

            Assert.Equal (4.0f, kseq.Sample (-200, 0));
            Assert.Equal (4.0f, kseq.Sample (100, 0));
            Assert.Equal (1.0f, kseq.Sample (200, 0));
            Assert.Equal (1.0f, kseq.Sample (600, 0));
        }

        /**
         * ValidRangeを(First > Last)に設定した時のテスト
         * さらにLoopモードに設定
         */
        [Fact]
        public void TestValidRangeLoop () {
            var kseq = new KeyframeSequence (4, 1, KeyframeSequence.Step);
            kseq.RepeatMode = KeyframeSequence.Loop;
            kseq.Duration = 500;
            kseq.SetValidRange (3, 0);

            //                   time             value
            kseq.SetKeyframe (0, 200, new float[] { 1.0f });
            kseq.SetKeyframe (1, 300, new float[] { 2.0f });  // 無効
            kseq.SetKeyframe (2, 400, new float[] { 3.0f });  // 無効
            kseq.SetKeyframe (3, 100, new float[] { 4.0f });

            Assert.Equal (1.0f, kseq.Sample (-200, 0));
            Assert.Equal (4.0f, kseq.Sample (100, 0));
            Assert.Equal (1.0f, kseq.Sample (200, 0));
            Assert.Equal (4.0f, kseq.Sample (600, 0));
        }

        /**
         * マルチチャンネルのテスト
         * */
        [Fact]
        public void TestMultiChannel () {
            var kseq = new KeyframeSequence (2, 4, 1, KeyframeSequence.Step);
            kseq.RepeatMode = KeyframeSequence.Constant;
            kseq.Duration = 500;
            //                       time
            kseq.SetKeyframeTime (0, 100);
            kseq.SetKeyframeTime (1, 200);
            kseq.SetKeyframeTime (2, 300);
            kseq.SetKeyframeTime (3, 400);

            //                  　ch　               value
            kseq.SetKeyframeValue (0, 0, new float[] { 1.0f }); // channel=0
            kseq.SetKeyframeValue (0, 1, new float[] { 2.0f });
            kseq.SetKeyframeValue (0, 2, new float[] { 3.0f });
            kseq.SetKeyframeValue (0, 3, new float[] { 4.0f });
            kseq.SetKeyframeValue (1, 0, new float[] { 5.0f }); // channel=1
            kseq.SetKeyframeValue (1, 1, new float[] { 6.0f });
            kseq.SetKeyframeValue (1, 2, new float[] { 7.0f });
            kseq.SetKeyframeValue (1, 3, new float[] { 8.0f });

            Assert.Equal (5.0f, kseq.Sample (100, 1));  // channel=1を選択
            Assert.Equal (6.0f, kseq.Sample (200, 1));
            Assert.Equal (6.0f, kseq.Sample (250, 1));
            Assert.Equal (7.0f, kseq.Sample (300, 1));
            Assert.Equal (8.0f, kseq.Sample (500, 1));
            Assert.Equal (8.0f, kseq.Sample (700, 1));
        }

        /**
         * 不正なキーフレームシーケンスのテスト
         * DurationがLastより小さい
         * */
        [Fact]
        public void TestIllegalDuration () {
            var kseq = new KeyframeSequence (2, 4, 1, KeyframeSequence.Step);
            kseq.RepeatMode = KeyframeSequence.Constant;
            kseq.Duration = 399;   // too small

            //                   time             value
            kseq.SetKeyframe (0, 100, new float[] { 1.0f });
            kseq.SetKeyframe (1, 200, new float[] { 2.0f });
            kseq.SetKeyframe (2, 300, new float[] { 3.0f });
            kseq.SetKeyframe (3, 400, new float[] { 4.0f });

            var thrown = false;
            try {
                kseq.Sample (100, 0);
            }
            catch (InvalidOperationException) {
                thrown = true;
            }

            Assert.Equal (true, thrown);
        }

        /**
         * 不正なキーフレームシーケンスのテスト
     　  * シーケンス時間が単調増加でない
         * */
        [Fact]
        public void TestIllegalKeyframeTime () {
            var kseq = new KeyframeSequence (2, 4, 1, KeyframeSequence.Step);
            kseq.RepeatMode = KeyframeSequence.Constant;
            kseq.Duration = 400;

            //                   time             value
            kseq.SetKeyframe (0, 100, new float[] { 1.0f });
            kseq.SetKeyframe (1, 200, new float[] { 2.0f });
            kseq.SetKeyframe (2, 300, new float[] { 3.0f });
            kseq.SetKeyframe (3, 299, new float[] { 4.0f });　// smaller than previous time.

            var thrown = false;
            try {
                kseq.Sample (100, 0);
            }
            catch (InvalidOperationException) {
                thrown = true;
            }

            Assert.Equal (true, thrown);
        }

        /**
         * 同一時間に複数のキーフレームがセットされている場合一番右のキーフレームが選択されるかのテスト
         * （この機能は値を非連続に変化させるときに使われる）
         * */
        [Fact]
        public void TestDegeneratedKeyframes () {

            var kseq = new KeyframeSequence (4, 1, KeyframeSequence.Linear);
            kseq.RepeatMode = KeyframeSequence.Constant;
            kseq.Duration = 500;

            //                   time             value
            kseq.SetKeyframe (0, 100, new float[] { 1.0f });
            kseq.SetKeyframe (1, 100, new float[] { 1.0f });
            kseq.SetKeyframe (2, 100, new float[] { 2.0f });
            kseq.SetKeyframe (3, 100, new float[] { 2.0f });

            Assert.Equal (1.0f, kseq.Sample (0, 0));
            Assert.Equal (2.0f, kseq.Sample (100, 0));  // t=100は2.0fを返すのが正しい
            Assert.Equal (2.0f, kseq.Sample (200, 0));

        }

        /**
         * AdditiveLoopで値が加算されていることをチェックするテスト
         * */
        [Fact]
        public void TestAdditiveLoop () {

            var kseq = new KeyframeSequence (4, 1, KeyframeSequence.Linear);
            kseq.RepeatMode = KeyframeSequence.AdditiveLoop;
            kseq.Duration = 500;

            //                   time             value
            kseq.SetKeyframe (0, 100, new float[] { 1.0f });
            kseq.SetKeyframe (1, 200, new float[] { 2.0f });
            kseq.SetKeyframe (2, 300, new float[] { 3.0f });
            kseq.SetKeyframe (3, 400, new float[] { 4.0f });

            Assert.Equal (1.0f, kseq.Sample (100, 0));
            Assert.Equal (4.0f, kseq.Sample (600, 0));
            Assert.Equal (-2.0f, kseq.Sample (-400, 0));

        }

    }
}
